package cn.monkey.io.transport.netty.server.codec.protocol;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.LinkedList;

public class SimpleMultipartServerProtocolSelector
        extends ChannelInboundHandlerAdapter
        implements MultipartServerProtocolSelector, ServerProtocolRouterFunctionRepository {

    private static final Logger log = LoggerFactory.getLogger(SimpleMultipartServerProtocolSelector.class);

    protected volatile LinkedList<ServerProtocolRouterFunction> protocolMatchers;

    public SimpleMultipartServerProtocolSelector() {
        protocolMatchers = new LinkedList<>();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (this.protocolMatchers.isEmpty()) {
            log.error("no protocol router function");
            ctx.close();
        }
        this.channelRead0(ctx, msg);
        super.channelRead(ctx, msg);
    }

    private void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        for (ServerProtocolRouterFunction protocolRouterFunction : this.protocolMatchers) {
            if (protocolRouterFunction.test(ctx.channel(), msg)) {
                protocolRouterFunction.accept(ctx.channel());
                ctx.pipeline().remove(this);
                return;
            }
        }
        log.error("no matching protocol routerFunction for ctx: {}, data: {}", ctx, msg);
        ctx.close();
    }

    @Override
    public void add(ServerProtocolRouterFunction protocolMatcher, int... port) {
        if (port.length == 0) {
            this.protocolMatchers.add(protocolMatcher);
            return;
        }
        this.protocolMatchers.add(new ServerPortBindingProtocolRouterFunction(protocolMatcher, port));
    }

    @Override
    public void remove(int port) {
        LinkedList<ServerProtocolRouterFunction> protocolMatchers = new LinkedList<>();
        for (ServerProtocolRouterFunction protocolMatcher : this.protocolMatchers) {
            if (protocolMatcher instanceof ServerPortBindingProtocolRouterFunction serverPortBindingProtocolMatcher) {
                int[] ports = serverPortBindingProtocolMatcher.ports();
                ports = Arrays.stream(ports)
                        .filter(p -> p != port)
                        .toArray();
                if (ports.length > 0) {
                    protocolMatchers.add(protocolMatcher);
                }
            }
        }
        this.protocolMatchers = protocolMatchers;
    }

    @Override
    public void remove(Class<ServerProtocolRouterFunction> clazz, int... ports) {
        LinkedList<ServerProtocolRouterFunction> protocolMatchers = new LinkedList<>();
        for (ServerProtocolRouterFunction protocolMatcher : this.protocolMatchers) {
            if (!clazz.isInstance(protocolMatcher)) {
                protocolMatchers.add(protocolMatcher);
                continue;
            }
            if (ports.length == 0) {
                continue;
            }
            if (protocolMatcher instanceof ServerPortBindingProtocolRouterFunction(
                    ServerProtocolRouterFunction delegate, int[] ports1
            )) {
                ports1 = Arrays.stream(ports1)
                        .filter(p -> {
                            for (int pp : ports) {
                                if (pp == p) {
                                    return true;
                                }
                            }
                            return false;
                        })
                        .toArray();
                if (ports1.length > 0) {
                    protocolMatchers.add(new ServerPortBindingProtocolRouterFunction(delegate, ports1));
                }
            }
        }
        this.protocolMatchers = protocolMatchers;
    }
}